本文將包含sideEffect可帶參數的情形
另外也包含範例與說明如下
sideEffect在中文裡翻譯成副作用,因此useEffect可以試著解釋成並非主要渲染的過程,這個hook實際上是指元件在瀏覽器中渲染完後要做的事情
。
useEffct是沒有回傳值,因此我們引入之後就直接使用(不像useState需要建立變數並解構),第一個參數帶入要在Effect所做的事情,第二個參數帶入依賴項(dependencise),通常取決於你要依賴於某個state。
使用空陣列讓其只在第一次渲染的時候執行
以下最簡單的範例
import React, { useState, useEffect } from 'react'
const App = () => {
useEffect(() => { console.log("我只印一次"); }, [])
const [number, setNumber] = useState(0);
const incrementHandler = () => {
setNumber((prev) => (prev + 1));
};
return (
<div>
<div>{number}</div>
<button onClick={incrementHandler}>+</button>
</div>
);
}
export default App
如下圖
我按下了五次的按鈕,畫面重新更新了五次,但是只做了一次useEffect
若想要讓每次畫面渲染都執行useEffect的話,第二個參數不帶任何東西
。這邊使用兩個state來作範例,藉此說明第二個參數是不依賴任何state的時候畫面都會更新。
import React, { useState, useEffect } from 'react'
const App = () => {
useEffect(() => { console.log("我印很多次"); })
const [number, setNumber] = useState(0);
const [string, setString] = useState("");
const incrementHandler = () => {
setNumber((prev) => (prev + 1));
};
return (
<div>
<div>{number}</div>
<button onClick={incrementHandler}>+</button>
<br />
<input type="text" onChange={(e) => setString(e.target.value)} />
<div>{string}</div>
</div>
);
}
export default App
這邊使用依賴於number這個state,可以看得出當我輸入字母,並沒有觸發useEffect,但我按下+的按鈕的時候,讓useEffect執行
import React, { useState, useEffect } from 'react'
const App = () => {
const [number, setNumber] = useState(0);
const [string, setString] = useState("");
useEffect(() => { console.log("我印很多次"); }, [number]);
const incrementHandler = () => {
setNumber((prev) => (prev + 1));
};
return (
<div>
<div>{number}</div>
<button onClick={incrementHandler}>+</button>
<br />
<input type="text" onChange={(e) => setString(e.target.value)} />
<div>{string}</div>
</div>
);
}
export default App
另外我們可以在useEffect帶入一個callback Function來建立當上一次的元件被銷毀前所要執行的事情,通常我們稱之為clarup函式,先看以下範例
import React, { useState, useEffect } from 'react'
const App = () => {
const [number, setNumber] = useState(0);
useEffect(() => {
console.log("元件渲染後數字是",
number)
return () => {
console.log("元件銷毀前數字是", number);
}
}, [number]);
const incrementHandler = () => {
setNumber((prev) => (prev + 1));
};
return (
<div>
<div>{number}</div>
<button onClick={incrementHandler}>+</button>
</div>
);
}
export default App
如下圖我們按下三次+號,整個流程會是
這個App元件被執行→
印出元件渲染後數字是0→
按下+號按鈕→
印出元件銷毀前數字是0→
印出數字印出元件渲染後數字是1→
按下+號按鈕→
印出元件銷毀前數字是1
印出數字印出元件渲染後數字是2→
按下+號按鈕→
印出元件銷毀前數字是2
印出數字印出元件渲染後數字是3→
一定要清除的狀況例如使用setInterval、訂閱某人要清除訂閱的時候
這個範例當元件render完畢後使用useEffect函式裡面加入setInterval以初始2秒印出時間,當按下按鈕的時候會由於number+1,因此會變成4秒印出時間,如果沒有清除setInterval就會造成擁有2個週期,一個是每2秒印出時間和另一個週期是每4秒也印出時間的狀況。
import React, { useState, useEffect } from 'react'
const App = () => {
const [number, setNumber] = useState(1);
useEffect(() => {
const interval = setInterval(() => {
const event = new Date();
console.log(event.toUTCString());
}, number * 2000);//每2秒會印出時間
return () => {
}
}, [number]);
const clickHandler = () => {
setNumber((pre) => (pre + 1));
}
return (
<div>
{number}
<br />
<button onClick={clickHandler}>按鈕</button>
</div>
)
}
export default App
如下圖
因此我們可以銷毀上次設置的setInterval
import React, { useState, useEffect } from 'react'
const App = () => {
const [number, setNumber] = useState(1);
useEffect(() => {
const interval = setInterval(() => {
const event = new Date();
console.log(event.toUTCString());
}, number * 2000);
return () => {
clearInterval(interval); //清除上一次的interval
}
}, [number]);
const clickHandler = () => {
setNumber((pre) => (pre + 1));
}
return (
<div>
{number}
<br />
<button onClick={clickHandler}>按鈕</button>
</div>
)
}
export default App
import React, { useState, useEffect } from 'react'
const App = () => {
const [timeCountdown, setCountdown] = useState(60);
useEffect(() => {
if (!timeCountdown) return;
const intervalId = setInterval(() => {
setCountdown((prev) => (prev - 1));
}, 1000);
return () => clearInterval(intervalId);
}, [timeCountdown]);
return (
<div>
<h1>{timeCountdown}</h1>
</div>
);
};
export default App;
我們分別在以下地方帶入console
程式碼如下
import React, { useState, useEffect } from 'react'
const App = () => {
console.log("我在AppFunction裡面");
const [number, setNumber] = useState(0);
const clickHandler = () => {
setNumber((prev) => (prev + 1));
}
useEffect(() => {
console.log("我在useEffect裡面");
return () => {
console.log("我在useEffect的callback裡面稱為clearup");
}
})
return (
<div>
{number}
{console.log("我在render函式裡面")}
<button onClick={clickHandler}>+</button>
</div>
);
}
export default App
最後渲染如下圖
初次載入可以發現順序如下(mount時期)
當我按下按鈕觸發第二次重新渲染的時候(update時期)
這邊有一篇完整介紹了useEffect的內容是redux的作者也是react團隊的成員之一
另外這裡講述一些關於fetch資料的方式
6 use cases of the useEffect ReactJS hook
使用 Effect Hook
A Simple Explanation of React.useEffect()
UseEffect dependency array and object comparison!